package gui;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.util.ArrayList;

import javax.swing.JComponent;

import undoredo.AbstractCommand;
import undoredo.AddEdgeCommand;
import engine.Edge;
import engine.Vertex;

@SuppressWarnings("serial")
public class View extends JComponent
{

   private static final Color black = Color.black;
   private static final Color blue = Color.blue;

   private static final Color red = Color.red;
   private static final Color white = Color.white;
   private ArrayList<Edge> edges = new ArrayList<Edge>();
   private int gap = 60;
   private final BasicStroke normalStrok = new BasicStroke(2);
   private Shape rec;
   private boolean selected;
   private int selectVertex = 0;
   private ArrayList<Vertex> vertices = new ArrayList<Vertex>();
   private final BasicStroke visitStrok = new BasicStroke(4);

   public View()
   {
      selected = false;
   }

   public void addEdge(Edge e)
   {
      Vertex first = e.getFirstNode();
      Vertex sec = e.getSecondNode();

      //Check if this edge exist in the first node
      if (first.isEdgeExist(e) == false)
         first.addEdge(e);
      // Check if this edge exist in the second node
      if (sec.isEdgeExist(e) == false)
         sec.addEdge(e);
      edges.add(e);
   }


   public void addEdge(int x, int y)
   {
      for (int i = 0; i < vertices.size(); i++)
         if (Math.abs(vertices.get(i).xPos() - x) < 20
               && Math.abs(vertices.get(i).yPos() - y) < 20)
            if (vertices.get(i).getColor() == white)
            {
               vertices.get(i).setColor(red);
               selectVertex++;
            }
            else if (vertices.get(i).getColor() == red)
            {
               vertices.get(i).setColor(white);
               selectVertex--;
            }
      if (selectVertex == 2)
      {
         selectVertex = 0;
         Vertex start = null, end = null;
         int j = 0;
         for (int i = 0; i < vertices.size(); i++)
            if (vertices.get(i).getColor() == red)
            {
               vertices.get(i).setColor(white);
               j++;
               if (j == 2)
                  end = vertices.get(i);
               else
                  start = vertices.get(i);
            }
         int tmp = (int) Math.sqrt(start.xPos() * end.xPos() + start.yPos()
               * end.yPos());

         if (isEdgesExist(start.getPoint(), end.getPoint()) == false)
         {
            Edge e = new Edge(start, end, tmp);

            // add edges into each node *********************
            start.addEdge(e);
            end.addEdge(e);

            AbstractCommand command = new AddEdgeCommand(this, e);
            Frame.addCommandToStack(command);
            edges.add(e);

         }
      }
   }

   public boolean addEdge(Vertex start, Vertex end, int weight)
   {
      Edge e = new Edge(start, end, weight);
      ArrayList<Edge> ee = start.getAllEdges();

      for(int i=0; i<ee.size(); i++)
         if(ee.get(i).isExist(end)==true)
            return false;

      AbstractCommand command = new AddEdgeCommand(this, e);
      command.execute();
      Frame.addCommandToStack(command);
      Frame.setUndoRedoVisible();

      return true;
   }

   public void addVertex(Vertex v)
   {
      vertices.add(v);
   }

   public void clear()
   {
      edges.clear();
      vertices.clear();
   }

   public void drawEdge(Edge e, Graphics2D g)
   {
      Vertex first = e.getFirstNode();
      Vertex second = e.getSecondNode();
      double hyp, x, y;
      int x1 = first.xPos();
      int y1 = first.yPos();
      int x2 = second.xPos();
      int y2 = second.yPos();
      x = x2 - x1;
      y = y2 - y1;
      hyp = Math.sqrt(x * x + y * y);
      if(e.getColor()==black)
         g.setStroke(visitStrok);
      else
         g.setStroke(normalStrok);
      g.setColor(e.getColor());

      g.drawLine(x1, y1, x2, y2);
      x1 = x1 + (int) ((x / hyp) * (hyp / 2));
      y1 = y1 + (int) ((y / hyp) * (hyp / 2));

      g.setColor(Color.yellow);

      //Rectangle back for the edge weight
      g.fillRect(x1 - 8, y1 - 8, 25, 15);
      g.setColor(black);

      if (e.getWeight() < 10)
         g.drawString(Integer.toString(e.getWeight()), x1, y1 + 5);
      else
         g.drawString(Integer.toString(e.getWeight()), x1 - 6, y1 + 5);

   }

   public void drawEdge(Vertex first, Vertex second, int weight, Color c,
         Graphics2D g)
   {
      double hyp, x, y;
      int x1 = first.xPos();
      int y1 = first.yPos();
      int x2 = second.xPos();
      int y2 = second.yPos();
      x = x2 - x1;
      y = y2 - y1;
      hyp = Math.sqrt(x * x + y * y);
      g.setColor(c);
      g.drawLine(x1, y1, x2, y2);
      x1 = x1 + (int) ((x / hyp) * (hyp / 2));
      y1 = y1 + (int) ((y / hyp) * (hyp / 2));
      g.setColor(Color.yellow);
      // g.fillOval(x1 - 8, y1 - 8, 25, 15);
      g.fillRect(x1 - 8, y1 - 8, 25, 15);
      g.setColor(black);
      if (weight < 10)
         g.drawString(Integer.toString(weight), x1, y1 + 5);
      else
         g.drawString(Integer.toString(weight), x1 - 6, y1 + 5);
   }

   public void drawNode(int x, int y, String name, Color c, Graphics2D g)
   {
      g.setColor(c); // set current color to the given color
      g.fillOval(x, y, 30, 30); // draw an oval which is filled with the
      // current color
      g.setColor(black); // set current color to black
      int pos = name.length();
      g.drawString(name, x + 13 - (pos * 3), y + 20);
   }

   public void drawSelection(Graphics2D g)
   {
      float[] dash1 = { 2f, 0f, 2f };
      BasicStroke bs1 = new BasicStroke(1, BasicStroke.CAP_BUTT,
            BasicStroke.JOIN_ROUND, 1.0f, dash1, 2f);
      g.setStroke(bs1);
      g.setColor(black);
      if (rec != null)
         g.draw(rec);

   }

   public ArrayList<Edge> getEdges()
   {
      return edges;
   }

   public int getMinEdge(Vertex vertex)
   {
      int tmp = 0;
      boolean found = false;
      for (int i = 0; i < edges.size(); i++)
         if ((vertex == edges.get(i).getFirstNode() || vertex == edges.get(i)
         .getSecondNode()))
         {
            tmp = i;

            edges.get(0).setColor(blue);
            if (edges.get(i).getColor() != blue && found)
               System.out.println(edges.get(i).getWeight());
            found = true;
         }

      return tmp;
   }

   public int getNumOfNode()
   {
      return vertices.size();
   }

   public boolean getSelectStatu()
   {
      return selected;
   }

   public ArrayList<Vertex> getVertices()
   {
      return vertices;
   }

   public boolean isAllNodeConnected()
   {
      for(int i=0; i<vertices.size(); i++)
         if(vertices.get(i).getAllEdges().size()==0)
            return false;
      return true;
   }

   public boolean isALlNodeVisited()
   {
      for (int i = 0; i < vertices.size(); i++)
         if (vertices.get(i).getStatus() != 2)
            return false;
      return true;
   }

   public int isEdge(int x, int y)
   {
      for (int i = 0; i < edges.size(); i++)
         if (Math.abs(edges.get(i).xPos() - x) < 15
               && Math.abs(edges.get(i).yPos() - y) < 15)
            return i; // return
      return -1; // return -1
   }

   public boolean isEdgesExist(Point p1, Point p2)
   {
      for (int i = 0; i < edges.size(); i++)
      {
         Point sp = edges.get(i).getStartPoint();
         Point ep = edges.get(i).getEndPoint();

         if (((sp.equals(p1) || sp.equals(p2)) && (ep.equals(p1) || ep
               .equals(p2))))
            return true;
      }

      return false;
   }

   public int isVertex(int x, int y)
   {
      for (int i = 0; i < vertices.size(); i++)
         if (Math.abs(vertices.get(i).xPos() - x) < 9
               && Math.abs(vertices.get(i).yPos() - y) < 9)
            return i;
      return -1; // return -1
   }

   public boolean isVertexTooClose(int x, int y)
   {
      for (int i = 0; i < vertices.size(); i++)
         if (Math.abs(vertices.get(i).xPos() - 15 - x) < gap
               && Math.abs(vertices.get(i).yPos() - 15 - y) < gap)
            return true; // return
      return false; // return -1
   }

   public void locate()
   {
      int x = 140, y = 125, xPos, yPos;
      double angle;

      if (vertices.size() < 9)
         vertices.add(new Vertex(0, 0, Integer.toString(vertices.size()))); // create
      // a
      angle = 360 / vertices.size(); // angle is equal to 360 over number of
      // vertex
      for (int i = 0; i < vertices.size(); i++)
      { // for all vertex
         angle -= 360 / vertices.size();
         xPos = x + (int) (100 * Math.cos((angle + 90) / 180 * Math.PI)); // calculate
         // x
         // position
         yPos = y - (int) (100 * Math.sin((angle + 90) / 180 * Math.PI)); // calculate
         // y
         // position
         vertices.get(i).setPosition(xPos, yPos); // set node position
      }
      repaint(); // call repaint
   }
   @Override
   public void paint(Graphics g2)
   {
      Graphics2D g = (Graphics2D) g2;
      g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
      g.setStroke(new BasicStroke(3.0f));

      g.setColor(Color.LIGHT_GRAY);
      g.fillRect(0, 0, getWidth(), getWidth());

      // System.out.println("Edges "+numEdges);
      for (int i = 0; i < edges.size(); i++)
      {
         Edge e = edges.get(i);
         drawEdge(e, g);

      }
      // System.out.println("Nodes Size"+vertices.size());
      for (int i = 0; i < vertices.size(); i++)
      {
         Vertex n = vertices.get(i);
         /* draw a node */
         drawNode(n.xPos() - 15, n.yPos() - 15, n.getName(), n.getColor(), g);
      }
      if (selected == true)
         drawSelection(g);
   }

   public boolean removeEdge(Edge e)
   {
      Vertex first = e.getFirstNode();
      Vertex sec = e.getSecondNode();

      first.remove(e);
      sec.remove(e);

      return edges.remove(e);
   }


   public boolean removeVertex(Vertex v)
   {
      return vertices.remove(v);

   }

   public void reset()
   {
      selectVertex = 0;
      for (int i = 0; i < edges.size(); i++)
         edges.get(i).setStatus(0);
      for (int i = 0; i < vertices.size(); i++)
      {
         vertices.get(i).resetVisitList();
         vertices.get(i).setStatus(0);
      }
   }

   public void resetSelection()
   {
      for (int i = 0; i < edges.size(); i++)
         if (edges.get(i).getStatus() == 1)
            edges.get(i).setStatus(edges.get(i).getRecentStatus());
      for (int i = 0; i < vertices.size(); i++)
         if (vertices.get(i).getStatus() == 1)
            vertices.get(i).setStatus(vertices.get(i).getRecentStatus());
      selected = false;
   }

   public void setAllEdgeStatusFromTo(int from, int to)
   {
      for (int i = 0; i < edges.size(); i++)
         if (edges.get(i).getStatus() == from)
            edges.get(i).setStatus(to);
   }

   public void setAllVertexStatusFromTo(int from, int to)
   {
      for (int i = 0; i < vertices.size(); i++)
         if (vertices.get(i).getStatus() == from)
            vertices.get(i).setStatus(to);
   }

   public void setEdges(ArrayList<Edge> edges)
   {
      this.edges = new ArrayList<Edge>();
      this.edges = edges;
   }

   public void setSelection(int x, int y, int width, int heigh)
   {
      selected = true;
      rec = new Rectangle(x, y, width, heigh);
   }

   public void setVertices(ArrayList<Vertex> vertices)
   {

      this.vertices = vertices;
   }
   public void sortEdge()
   {
      Edge tmp;
      for (int i = 0; i < edges.size(); i++)
         for (int j = 0; j < edges.size(); j++)
            if (edges.get(i).getWeight() < edges.get(j).getWeight())
            {
               tmp = edges.get(i);
               edges.set(i, edges.get(j));
               edges.set(j, tmp);
            }
   }
}
